#!/usr/bin/env python3
"""
JDFS2 系统集成测试脚本

这个脚本通过直接调用各种 S3 Client 类来执行完整的系统集成测试，
按照逻辑顺序测试所有实现的功能。如果任何一个步骤失败，脚本会退出并打印错误信息。

测试流程：
1. 服务器连接测试
2. Bucket 管理测试
3. 对象操作测试
4. 多部分上传测试
5. 高级功能测试（生命周期、版本控制等）
6. 清理测试数据

使用方法：
    python sys_test.py [--host HOST] [--cleanup-only]
"""

import sys
import os
import time
import tempfile
import argparse
import logging
import hashlib
from pathlib import Path

# 导入各种Client类
from s3_bucket_list import BucketListClient
from s3_bucket_create import BucketCreateClient
from s3_bucket_delete import BucketDeleteClient
from s3_bucket_head import BucketHeadClient
from s3_bucket_location import BucketLocationClient
from s3_bucket_versioning import BucketVersioningClient
from s3_bucket_lifecycle import BucketLifecycleClient
from s3_object_put import ObjectPutClient
from s3_object_get import ObjectGetClient
from s3_object_delete import ObjectDeleteClient
from s3_object_head import ObjectHeadClient
from s3_object_list import ObjectListClient
from s3_object_copy import ObjectCopyClient
from s3_object_content_md5 import ContentMD5Client
from s3_multipart_upload import MultipartUploadClient

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout)
    ]
)

# 测试配置
DEFAULT_HOST = 'http://localhost:8000'
DEFAULT_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE'
DEFAULT_SECRET_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'

# 测试用的桶名和对象名
TEST_BUCKET = 'sys-test-bucket'
TEST_BUCKET_2 = 'sys-test-bucket-2'
TEST_OBJECTS = [
    'test-object-1.txt',
    'test-object-2.json',
    'test-object-3.bin',
    'folder/test-object-4.txt',
    'folder/subfolder/test-object-5.txt'
]


class SystemTester:
    """系统集成测试器"""
    
    def __init__(self, host=DEFAULT_HOST, access_key=DEFAULT_ACCESS_KEY, secret_key=DEFAULT_SECRET_KEY):
        self.host = host
        self.access_key = access_key
        self.secret_key = secret_key
        self.temp_files = []
        
        # 初始化各种Client
        self.bucket_list_client = BucketListClient(host, access_key, secret_key)
        self.bucket_create_client = BucketCreateClient(host, access_key, secret_key)
        self.bucket_delete_client = BucketDeleteClient(host, access_key, secret_key)
        self.bucket_head_client = BucketHeadClient(host, access_key, secret_key)
        self.bucket_location_client = BucketLocationClient(host, access_key, secret_key)
        self.bucket_versioning_client = BucketVersioningClient(host, access_key, secret_key)
        self.bucket_lifecycle_client = BucketLifecycleClient(host, access_key, secret_key)
        self.object_put_client = ObjectPutClient(host, access_key, secret_key)
        self.object_get_client = ObjectGetClient(host, access_key, secret_key)
        self.object_delete_client = ObjectDeleteClient(host, access_key, secret_key)
        self.object_head_client = ObjectHeadClient(host, access_key, secret_key)
        self.object_list_client = ObjectListClient(host, access_key, secret_key)
        self.object_copy_client = ObjectCopyClient(host, access_key, secret_key)
        self.content_md5_client = ContentMD5Client(host, access_key, secret_key)
        self.multipart_upload_client = MultipartUploadClient(host, access_key, secret_key)
    
    def create_temp_file(self, content, suffix='.txt'):
        """创建临时文件"""
        fd, path = tempfile.mkstemp(suffix=suffix)
        with os.fdopen(fd, 'w') as f:
            f.write(content)
        self.temp_files.append(path)
        return path
    
    def create_temp_binary_file(self, content, suffix='.bin'):
        """创建临时二进制文件"""
        fd, path = tempfile.mkstemp(suffix=suffix)
        with os.fdopen(fd, 'wb') as f:
            f.write(content)
        self.temp_files.append(path)
        return path
    
    def cleanup_temp_files(self):
        """清理临时文件"""
        for temp_file in self.temp_files:
            try:
                os.unlink(temp_file)
            except:
                pass
        self.temp_files.clear()
    
    def test_server_connection(self):
        """测试服务器连接"""
        logging.info("=" * 60)
        logging.info("🚀 开始服务器连接测试")
        logging.info("=" * 60)
        
        try:
            # 测试列出桶（这会验证服务器连接）
            logging.info("🔧 测试服务器连接（通过列出桶）")
            success = self.bucket_list_client.list_buckets()
            
            if success:
                logging.info("✅ 服务器连接测试成功")
                return True
            else:
                logging.error("❌ 服务器连接测试失败")
                return False
                
        except Exception as e:
            logging.error(f"❌ 服务器连接测试异常: {e}")
            return False
    
    def test_bucket_management(self):
        """测试桶管理功能"""
        logging.info("=" * 60)
        logging.info("🪣 开始桶管理测试")
        logging.info("=" * 60)
        
        try:
            # 1. 列出现有桶
            logging.info("🔧 列出现有桶")
            if not self.bucket_list_client.list_buckets():
                return False
            
            # 2. 创建测试桶
            logging.info(f"🔧 创建测试桶 {TEST_BUCKET}")
            if not self.bucket_create_client.create_single_bucket(TEST_BUCKET):
                return False
            
            # 3. 检查桶是否存在
            logging.info(f"🔧 检查桶 {TEST_BUCKET} 是否存在")
            if not self.bucket_head_client.head_single_bucket(TEST_BUCKET):
                return False
            
            # 4. 获取桶位置
            logging.info(f"🔧 获取桶 {TEST_BUCKET} 位置")
            if not self.bucket_location_client.get_single_bucket_location(TEST_BUCKET):
                return False
            
            # 5. 创建第二个桶用于测试
            logging.info(f"🔧 创建第二个测试桶 {TEST_BUCKET_2}")
            if not self.bucket_create_client.create_single_bucket(TEST_BUCKET_2):
                return False
            
            logging.info("✅ 桶管理测试成功")
            return True
            
        except Exception as e:
            logging.error(f"❌ 桶管理测试异常: {e}")
            return False
    
    def test_object_operations(self):
        """测试对象操作功能"""
        logging.info("=" * 60)
        logging.info("📁 开始对象操作测试")
        logging.info("=" * 60)
        
        # 创建测试文件
        test_content_1 = "这是测试对象1的内容\n包含中文和英文 English content"
        test_file_1 = self.create_temp_file(test_content_1, '.txt')
        
        test_content_2 = '{"test": "json", "number": 123, "array": [1, 2, 3]}'
        test_file_2 = self.create_temp_file(test_content_2, '.json')
        
        # 创建二进制测试文件
        binary_content = b'\x00\x01\x02\x03\x04\x05' * 1000
        binary_file = self.create_temp_binary_file(binary_content, '.bin')
        
        try:
            # 1. 上传对象
            logging.info(f"🔧 上传文本对象 {TEST_OBJECTS[0]}")
            with open(test_file_1, 'rb') as f:
                body = f.read()
            if not self.object_put_client.put_object(
                bucket_name=TEST_BUCKET, 
                key=TEST_OBJECTS[0], 
                body=body,
                content_type='text/plain; charset=utf-8'
            ):
                return False
            
            # 2. 上传 JSON 对象
            logging.info(f"🔧 上传 JSON 对象 {TEST_OBJECTS[1]}")
            with open(test_file_2, 'rb') as f:
                body = f.read()
            if not self.object_put_client.put_object(
                bucket_name=TEST_BUCKET, 
                key=TEST_OBJECTS[1], 
                body=body,
                content_type='application/json'
            ):
                return False
            
            # 3. 上传二进制对象
            logging.info(f"🔧 上传二进制对象 {TEST_OBJECTS[2]}")
            with open(binary_file, 'rb') as f:
                body = f.read()
            if not self.object_put_client.put_object(
                bucket_name=TEST_BUCKET, 
                key=TEST_OBJECTS[2], 
                body=body,
                content_type='application/octet-stream'
            ):
                return False
            
            # 4. 上传带元数据的对象
            logging.info(f"🔧 上传带元数据的对象 {TEST_OBJECTS[3]}")
            metadata = {'author': 'test', 'version': '1.0'}
            with open(test_file_1, 'rb') as f:
                body = f.read()
            if not self.object_put_client.put_object(
                bucket_name=TEST_BUCKET, 
                key=TEST_OBJECTS[3], 
                body=body,
                metadata=metadata
            ):
                return False
            
            # 5. 检查对象是否存在
            logging.info(f"🔧 检查对象 {TEST_OBJECTS[0]} 是否存在")
            if not self.object_head_client.head_object(TEST_BUCKET, TEST_OBJECTS[0]):
                return False
            
            # 6. 下载对象
            logging.info(f"🔧 下载对象 {TEST_OBJECTS[0]}")
            download_file = self.create_temp_file('', '_downloaded.txt')
            if not self.object_get_client.get_object(
                bucket_name=TEST_BUCKET, 
                key=TEST_OBJECTS[0], 
                output_file=download_file
            ):
                return False
            
            # 验证下载的内容
            with open(download_file, 'r', encoding='utf-8') as f:
                downloaded_content = f.read()
            if downloaded_content != test_content_1:
                logging.error(f"❌ 下载内容不匹配")
                return False
            logging.info("✅ 下载内容验证成功")
            
            # 7. 列出桶中的对象
            logging.info(f"🔧 列出桶 {TEST_BUCKET} 中的对象")
            if not self.object_list_client.list_objects(TEST_BUCKET):
                return False
            
            # 8. 复制对象
            logging.info(f"🔧 复制对象到 {TEST_BUCKET_2}")
            if not self.object_copy_client.copy_object(
                src_bucket=TEST_BUCKET, 
                src_key=TEST_OBJECTS[0],
                dst_bucket=TEST_BUCKET_2, 
                dst_key='copied-object.txt'
            ):
                return False
            
            # 9. 测试 Content-MD5 验证
            logging.info("🔧 测试 Content-MD5 验证功能")
            if not self.content_md5_client.run_all_tests(TEST_BUCKET):
                return False
            
            logging.info("✅ 对象操作测试成功")
            return True
            
        except Exception as e:
            logging.error(f"❌ 对象操作测试异常: {e}")
            return False
        finally:
            self.cleanup_temp_files()
    
    def test_multipart_upload(self):
        """测试多部分上传功能"""
        logging.info("=" * 60)
        logging.info("📤 开始多部分上传测试")
        logging.info("=" * 60)
        
        # 创建一个大文件用于多部分上传测试
        large_content = "A" * (10 * 1024 * 1024)  # 10MB
        large_file = self.create_temp_file(large_content, '.bin')
        
        try:
            # 1. 多部分上传
            logging.info("🔧 多部分上传大文件")
            if not self.multipart_upload_client.upload_file(
                bucket=TEST_BUCKET, 
                key='large-file.bin', 
                file_path=large_file,
                part_size=5242880  # 5MB per part
            ):
                return False
            
            # 2. 验证上传的文件
            logging.info("🔧 验证多部分上传的文件")
            if not self.object_head_client.head_object(TEST_BUCKET, 'large-file.bin'):
                return False
            
            logging.info("✅ 多部分上传测试成功")
            return True
            
        except Exception as e:
            logging.error(f"❌ 多部分上传测试异常: {e}")
            return False
        finally:
            self.cleanup_temp_files()
    
    def test_advanced_features(self):
        """测试高级功能"""
        logging.info("=" * 60)
        logging.info("🔧 开始高级功能测试")
        logging.info("=" * 60)
        
        try:
            # 1. 测试桶版本控制
            logging.info("🔧 获取桶版本控制状态")
            if not self.bucket_versioning_client.get_versioning_status(TEST_BUCKET):
                return False
            
            # 2. 设置桶版本控制
            logging.info("🔧 启用桶版本控制")
            if not self.bucket_versioning_client.set_versioning_status(TEST_BUCKET, 'Enabled'):
                return False
            
            # 3. 测试生命周期配置
            logging.info("🔧 测试生命周期配置")
            if not self.bucket_lifecycle_client.get_lifecycle(TEST_BUCKET):
                return False
            
            logging.info("✅ 高级功能测试成功")
            return True
            
        except Exception as e:
            logging.error(f"❌ 高级功能测试异常: {e}")
            return False
    
    def test_error_handling(self):
        """测试错误处理"""
        logging.info("=" * 60)
        logging.info("⚠️  开始错误处理测试")
        logging.info("=" * 60)
        
        try:
            # 1. 尝试访问不存在的桶
            logging.info("🔧 访问不存在的桶（应该失败）")
            success, message = self.bucket_head_client.head_single_bucket('non-existent-bucket')
            if success:
                logging.error("❌ 访问不存在的桶应该失败但成功了")
                return False
            logging.info("✅ 访问不存在的桶正确失败")
            
            # 2. 尝试访问不存在的对象
            logging.info("🔧 访问不存在的对象（应该失败）")
            result = self.object_head_client.head_object(TEST_BUCKET, 'non-existent-object')
            if result:
                logging.error("❌ 访问不存在的对象应该失败但成功了")
                return False
            logging.info("✅ 访问不存在的对象正确失败")
            
            # 3. 尝试删除不存在的对象（S3 标准行为是返回成功）
            logging.info("🔧 删除不存在的对象（S3 标准行为）")
            if not self.object_delete_client.delete_single_object(TEST_BUCKET, 'non-existent-object'):
                logging.error("❌ 删除不存在的对象失败")
                return False
            logging.info("✅ 删除不存在的对象成功（S3 标准行为）")
            
            logging.info("✅ 错误处理测试成功")
            return True
            
        except Exception as e:
            logging.error(f"❌ 错误处理测试异常: {e}")
            return False
    
    def cleanup_test_data(self):
        """清理测试数据"""
        logging.info("=" * 60)
        logging.info("🧹 开始清理测试数据")
        logging.info("=" * 60)
        
        try:
            # 1. 删除测试对象
            for obj_key in TEST_OBJECTS:
                logging.info(f"🔧 删除测试对象 {obj_key}")
                self.object_delete_client.delete_single_object(TEST_BUCKET, obj_key)
            
            # 删除多部分上传的文件
            logging.info("🔧 删除多部分上传的文件")
            self.object_delete_client.delete_single_object(TEST_BUCKET, 'large-file.bin')
            
            # 删除复制的对象
            logging.info("🔧 删除复制的对象")
            self.object_delete_client.delete_single_object(TEST_BUCKET_2, 'copied-object.txt')
            
            # 2. 删除生命周期配置
            logging.info("🔧 删除桶生命周期配置")
            self.bucket_lifecycle_client.delete_lifecycle(TEST_BUCKET)
            
            # 3. 删除测试桶
            logging.info(f"🔧 删除测试桶 {TEST_BUCKET}")
            self.bucket_delete_client.delete_single_bucket(TEST_BUCKET, force=True)
            
            logging.info(f"🔧 删除测试桶 {TEST_BUCKET_2}")
            self.bucket_delete_client.delete_single_bucket(TEST_BUCKET_2, force=True)
            
            logging.info("✅ 测试数据清理完成")
            
        except Exception as e:
            logging.error(f"❌ 清理测试数据异常: {e}")
    
    def run_all_tests(self):
        """运行所有测试"""
        start_time = time.time()
        
        try:
            # 测试步骤
            test_steps = [
                ("服务器连接测试", self.test_server_connection),
                ("桶管理测试", self.test_bucket_management),
                ("对象操作测试", self.test_object_operations),
                ("多部分上传测试", self.test_multipart_upload),
                ("高级功能测试", self.test_advanced_features),
                ("错误处理测试", self.test_error_handling),
            ]
            
            passed_steps = 0
            total_steps = len(test_steps)
            
            for step_name, step_func in test_steps:
                logging.info(f"\n🔄 开始 {step_name}")
                if step_func():
                    passed_steps += 1
                    logging.info(f"✅ {step_name} 通过")
                else:
                    logging.error(f"❌ {step_name} 失败")
                    logging.error("测试失败，停止执行")
                    return False
            
            # 测试完成
            end_time = time.time()
            duration = end_time - start_time
            
            logging.info("=" * 60)
            logging.info("🎉 所有测试完成")
            logging.info("=" * 60)
            logging.info(f"✅ 通过: {passed_steps}/{total_steps}")
            logging.info(f"⏱️  耗时: {duration:.2f} 秒")
            logging.info("🎯 系统集成测试全部通过！")
            
            return True
            
        except KeyboardInterrupt:
            logging.info("\n⚠️  测试被用户中断")
            return False
        except Exception as e:
            logging.error(f"❌ 测试过程中发生异常: {e}")
            return False
        finally:
            # 清理临时文件
            self.cleanup_temp_files()


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='JDFS2 系统集成测试脚本',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # 运行完整测试
  python sys_test.py
  
  # 指定服务器地址
  python sys_test.py --host http://your-server:8000
  
  # 仅清理测试数据
  python sys_test.py --cleanup-only
  
  # 详细输出
  python sys_test.py --verbose
        """
    )
    
    parser.add_argument('--host', default=DEFAULT_HOST, 
                       help=f'S3 服务端点 (默认: {DEFAULT_HOST})')
    parser.add_argument('--access-key', default=DEFAULT_ACCESS_KEY, 
                       help='AWS Access Key')
    parser.add_argument('--secret-key', default=DEFAULT_SECRET_KEY, 
                       help='AWS Secret Key')
    parser.add_argument('--cleanup-only', action='store_true', 
                       help='仅清理测试数据')
    parser.add_argument('--verbose', '-v', action='store_true', 
                       help='详细输出')
    
    args = parser.parse_args()
    
    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)
    
    tester = SystemTester(args.host, args.access_key, args.secret_key)
    
    if args.cleanup_only:
        tester.cleanup_test_data()
        return 0
    
    # 运行测试
    success = tester.run_all_tests()
    
    # 清理测试数据
    if success:
        tester.cleanup_test_data()
    
    return 0 if success else 1


if __name__ == '__main__':
    sys.exit(main())